package cn.chen.utils;

import cn.chen.utils.bean.Formula;
import com.udojava.evalex.Expression;
import org.apache.commons.collections4.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class FormulaUtils {

    /**
     * 提取变量
     */
    private static Set<String> extractVar(Expression expression) {
        return new HashSet<>(expression.getUsedVariables());
    }

    /**
     * 提取可展开的项
     * @param formula 公式
     * @param ignoreStartWith 忽略以xxx开头的
     */
    private static Set<String> extractExpansion(String formula, String ignoreStartWith) {
        Set<String> vars = extractVar(new Expression(formula));
        if (ignoreStartWith != null && !ignoreStartWith.trim().isEmpty()) {
            return vars.stream()
                    .filter(v -> !v.startsWith(ignoreStartWith))
                    .collect(Collectors.toSet());
        } else {
            return vars;
        }
    }

    /**
     * 展开公式
     * @param formula 公式
     * @param ignoreStartWith 忽略以xxx开头的
     * @param formulaList 所有的公式
     */
    public static String expansion(String formula, String ignoreStartWith, List<Formula> formulaList) {
        Map<String, String> formulas = formulaList.stream().collect(
                Collectors.toMap(Formula::getName, Formula::getExpression));
        Set<String> expansionSet;
        do {
            expansionSet = extractExpansion(formula, ignoreStartWith);
            for (String expansion : expansionSet) {
                formula = formula.replace(expansion, "(" + formulas.get(expansion) + ")");
            }
        } while (!CollectionUtils.isEmpty(expansionSet));
        return formula;
    }

    /**
     * 计算公式
     * @param formula 公式
     * @param varValueSupplier 变量值
     */
    public static BigDecimal calculate(String formula, Function<Set<String>, Map<String, BigDecimal>> varValueSupplier) {
        Expression expression = new Expression(formula);
        Set<String> vars = extractVar(expression);
        Map<String, BigDecimal> varValues = varValueSupplier.apply(vars);
        varValues.forEach(expression::with);
        return expression.eval();
    }

    /**
     * 展开公式（批量）
     * @param formulas 公式
     * @param ignoreStartWith 忽略以xxx开头的
     * @param formulaList 所有的公式
     */
    public static Set<String> expansionBatch(List<String> formulas, String ignoreStartWith, List<Formula> formulaList) {
        return formulas.stream()
                .map(formula -> expansion(formula, ignoreStartWith, formulaList))
                .collect(Collectors.toSet());
    }

    /**
     * 计算公式（批量）
     * @param formulas 公式
     * @param varValueSupplier 变量值
     */
    public static Map<String, BigDecimal> calculateBatch(Set<String> formulas, Function<Set<String>, Map<String, BigDecimal>> varValueSupplier) {
        Map<String, BigDecimal> resultMap = new HashMap<>();
        for (String formula : formulas) {
            resultMap.put(formula, calculate(formula, varValueSupplier));
        }
        return resultMap;
    }
}